/*
 * Copyright (c) 2015, Nordic Semiconductor
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package no.nordicsemi.android.dfuupdate.dfu;

import android.app.LoaderManager.LoaderCallbacks;
import android.bluetooth.BluetoothAdapter;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.webkit.MimeTypeMap;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import com.alibaba.fastjson.JSON;

import java.io.File;
import java.util.ArrayList;

import no.nordicsemi.android.dfu.DfuServiceInitiator;
import no.nordicsemi.android.dfuupdate.AppHelpFragment;
import no.nordicsemi.android.dfuupdate.R;
import no.nordicsemi.android.dfuupdate.ToolboxApplication;
import no.nordicsemi.android.dfuupdate.dfu.adapter.FileBrowserAppsAdapter;
import no.nordicsemi.android.dfuupdate.dfu.fragment.UploadCancelFragment;
import no.nordicsemi.android.dfuupdate.dfu.fragment.ZipInfoFragment;
import no.nordicsemi.android.dfuupdate.dfu.settings.SettingsActivity;
import no.nordicsemi.android.dfuupdate.scanner.ScannerFragment;

/**
 * DfuActivity is the main DFU activity It implements DFUManagerCallbacks to receive callbacks from
 * DfuManager class It implements DeviceScannerFragment.OnDeviceSelectedListener callback to receive callback when device is selected from scanning dialog The activity supports portrait and
 * landscape orientations
 */
public class SelectedFileActivity extends AppCompatActivity implements LoaderCallbacks<Cursor>,
        UploadCancelFragment.CancelFragmentListener {
    private static final String TAG = "SelectedFileActivity";

    private static final String PREFS_DEVICE_NAME = "no.nordicsemi.android.nrftoolbox.dfu.PREFS_DEVICE_NAME";
    private static final String PREFS_FILE_NAME = "no.nordicsemi.android.nrftoolbox.dfu.PREFS_FILE_NAME";
    private static final String PREFS_FILE_TYPE = "no.nordicsemi.android.nrftoolbox.dfu.PREFS_FILE_TYPE";
    private static final String PREFS_FILE_SCOPE = "no.nordicsemi.android.nrftoolbox.dfu.PREFS_FILE_SCOPE";
    private static final String PREFS_FILE_SIZE = "no.nordicsemi.android.nrftoolbox.dfu.PREFS_FILE_SIZE";

    private static final String DATA_DEVICE = "device";
    private static final String DATA_FILE_TYPE = "file_type";
    private static final String DATA_FILE_TYPE_TMP = "file_type_tmp";
    private static final String DATA_FILE_PATH = "file_path";
    private static final String DATA_FILE_STREAM = "file_stream";
    private static final String DATA_INIT_FILE_PATH = "init_file_path";
    private static final String DATA_INIT_FILE_STREAM = "init_file_stream";
    private static final String DATA_STATUS = "status";
    private static final String DATA_SCOPE = "scope";
    private static final String DATA_DFU_COMPLETED = "dfu_completed";
    private static final String DATA_DFU_ERROR = "dfu_error";

    private static final String EXTRA_URI = "uri";

    private static final int ENABLE_BT_REQ = 0;
    private static final int SELECT_FILE_REQ = 1;
    private static final int SELECT_INIT_FILE_REQ = 2;

    private TextView fileNameView;
    private TextView fileTypeView;
    private TextView fileScopeView;
    private TextView fileSizeView;
    private TextView fileStatusView;


    private Button selectFileButton, startUpdateButton;

    private String filePath;
    private Uri fileStreamUri;
    private String initFilePath;
    private Uri initFileStreamUri;
    private int fileType;
    private int fileTypeTmp; // This value is being used when user is selecting a file not to overwrite the old value (in case he/she will cancel selecting file)
    private Integer scope;
    private boolean statusOk;

    private ArrayList<String> selectedDevices;

    /**
     * The progress listener receives events from the DFU Service.
     * If is registered in onCreate() and unregistered in onDestroy() so methods here may also be called
     * when the screen is locked or the app went to the background. This is because the UI needs to have the
     * correct information after user comes back to the activity and this information can't be read from the service
     * as it might have been killed already (DFU completed or finished with error).
     */


    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_select_file);
        isBLESupported();
        if (!isBLEEnabled()) {
            showBLEDialog();
        }

        Bundle bundle = getIntent().getExtras();
        selectedDevices = bundle.getStringArrayList("SELECTED_DEVICES");
        Log.d(TAG, JSON.toJSONString(selectedDevices));

        setGUI();

//        if(ToolboxApplication.fileUri != null) {
//            Log.d("FILE", "fileUri is not null");
//            Log.d("FILE", JSON.toJSONString(ToolboxApplication.fileUri));
//            fileStreamUri = ToolboxApplication.fileUri;
//        }


        // restore saved state
        fileType = DfuService.TYPE_AUTO; // Default
        if (savedInstanceState != null) {
            fileType = savedInstanceState.getInt(DATA_FILE_TYPE);
            fileTypeTmp = savedInstanceState.getInt(DATA_FILE_TYPE_TMP);
            filePath = savedInstanceState.getString(DATA_FILE_PATH);
            fileStreamUri = savedInstanceState.getParcelable(DATA_FILE_STREAM);
            initFilePath = savedInstanceState.getString(DATA_INIT_FILE_PATH);
            initFileStreamUri = savedInstanceState.getParcelable(DATA_INIT_FILE_STREAM);
            statusOk = statusOk || savedInstanceState.getBoolean(DATA_STATUS);
            scope = savedInstanceState.containsKey(DATA_SCOPE) ? savedInstanceState.getInt(DATA_SCOPE) : null;
            startUpdateButton.setEnabled(statusOk);
        }

        startUpdateButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onUpdateStart();
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(@NonNull final Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(DATA_FILE_TYPE, fileType);
        outState.putInt(DATA_FILE_TYPE_TMP, fileTypeTmp);
        outState.putString(DATA_FILE_PATH, filePath);
        outState.putParcelable(DATA_FILE_STREAM, fileStreamUri);
        outState.putString(DATA_INIT_FILE_PATH, initFilePath);
        outState.putParcelable(DATA_INIT_FILE_STREAM, initFileStreamUri);
        outState.putBoolean(DATA_STATUS, statusOk);
        if (scope != null) outState.putInt(DATA_SCOPE, scope);
    }

    private void setGUI() {
        final Toolbar toolbar = findViewById(R.id.toolbar_actionbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);


        fileNameView = findViewById(R.id.file_name);
        fileTypeView = findViewById(R.id.file_type);
        fileScopeView = findViewById(R.id.file_scope);
        fileSizeView = findViewById(R.id.file_size);
        fileStatusView = findViewById(R.id.file_status);
        selectFileButton = findViewById(R.id.action_select_file);
        startUpdateButton = findViewById(R.id.start_upload);
        if(statusOk) {
            startUpdateButton.setEnabled(true);
        }
        else {
            startUpdateButton.setEnabled(false);
        }

        final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
    }

    @Override
    protected void onResume() {
        super.onResume();

    }

    @Override
    protected void onPause() {
        super.onPause();
    }

    private void isBLESupported() {
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            showToast(R.string.no_ble);
            finish();
        }
    }

    private boolean isBLEEnabled() {
        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
        return adapter != null && adapter.isEnabled();
    }

    private void showBLEDialog() {
        final Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableIntent, ENABLE_BT_REQ);
    }

    private void showDeviceScanningDialog() {
        final ScannerFragment dialog = ScannerFragment.getInstance(null); // Device that is advertising directly does not have the GENERAL_DISCOVERABLE nor LIMITED_DISCOVERABLE flag set.
        dialog.show(getSupportFragmentManager(), "scan_fragment");
    }

    @Override
    public boolean onCreateOptionsMenu(final Menu menu) {
        getMenuInflater().inflate(R.menu.settings_and_about, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                //startActivity(new Intent(SelectedFileActivity.this, ScanBatchActivity.class));
                onBackPressed();
                break;
            case R.id.action_about:
                final AppHelpFragment fragment = AppHelpFragment.getInstance(R.string.dfu_about_text);
                fragment.show(getSupportFragmentManager(), "help_fragment");
                break;
            case R.id.action_settings:
                final Intent intent = new Intent(this, SettingsActivity.class);
                startActivity(intent);
                break;
        }
        return true;
    }

    @Override
    protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Log.d("DATA", JSON.toJSONString(data));

        Log.d("CODE", "REQ=" + requestCode + ", RST="+resultCode);
        if (resultCode != RESULT_OK)
            return;

        switch (requestCode) {
            case SELECT_FILE_REQ: {
                // clear previous data
                fileType = fileTypeTmp;
                filePath = null;
                fileStreamUri = null;

                // and read new one
                final Uri uri = data.getData();
                Log.d("uri", JSON.toJSONString(data.getData()));
                /*
                 * The URI returned from application may be in 'file' or 'content' schema. 'File' schema allows us to create a File object and read details from if
                 * directly. Data from 'Content' schema must be read by Content Provider. To do that we are using a Loader.
                 */
                if (uri.getScheme().equals("file")) {
                    // the direct path to the file has been returned
                    final String path = uri.getPath();
                    final File file = new File(path);
                    filePath = path;
                    Log.d("FILE PATH", path);
                    updateFileInfo(file.getName(), file.length(), fileType);
                } else if (uri.getScheme().equals("content")) {
                    // an Uri has been returned
                    fileStreamUri = uri;
                    ToolboxApplication.fileUri = uri;
                    Log.d("FILE URI", uri.toString());
                    // if application returned Uri for streaming, let's us it. Does it works?
                    // FIXME both Uris works with Google Drive app. Why both? What's the difference? How about other apps like DropBox?
                    final Bundle extras = data.getExtras();
                    if (extras != null && extras.containsKey(Intent.EXTRA_STREAM)) {
                        fileStreamUri = extras.getParcelable(Intent.EXTRA_STREAM);
                        Log.d("FILE PARCELABLE", extras.getParcelable(Intent.EXTRA_STREAM).toString());
                    }
                    // file name and size must be obtained from Content Provider
                    final Bundle bundle = new Bundle();
                    bundle.putParcelable(EXTRA_URI, uri);
                    getLoaderManager().restartLoader(SELECT_FILE_REQ, bundle, this);
                }
                break;
            }
            case SELECT_INIT_FILE_REQ: {
                initFilePath = null;
                initFileStreamUri = null;

                // and read new one
                final Uri uri = data.getData();
                /*
                 * The URI returned from application may be in 'file' or 'content' schema. 'File' schema allows us to create a File object and read details from if
                 * directly. Data from 'Content' schema must be read by Content Provider. To do that we are using a Loader.
                 */
                if (uri.getScheme().equals("file")) {
                    // the direct path to the file has been returned
                    initFilePath = uri.getPath();
                    fileStatusView.setText(R.string.dfu_file_status_ok_with_init);
                } else if (uri.getScheme().equals("content")) {
                    // an Uri has been returned
                    initFileStreamUri = uri;
                    // if application returned Uri for streaming, let's us it. Does it works?
                    // FIXME both Uris works with Google Drive app. Why both? What's the difference? How about other apps like DropBox?
                    final Bundle extras = data.getExtras();
                    if (extras != null && extras.containsKey(Intent.EXTRA_STREAM))
                        initFileStreamUri = extras.getParcelable(Intent.EXTRA_STREAM);
                    fileStatusView.setText(R.string.dfu_file_status_ok_with_init);
                }
                break;
            }
            default:
                break;
        }
        statusOk = (fileStreamUri != null);
    }

    @Override
    public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
        final Uri uri = args.getParcelable(EXTRA_URI);
        /*
         * Some apps, f.e. Google Drive allow to select file that is not on the device. There is no "_data" column handled by that provider. Let's try to obtain
         * all columns and than check which columns are present.
         */
        // final String[] projection = new String[] { MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.MediaColumns.SIZE, MediaStore.MediaColumns.DATA };
        return new CursorLoader(this, uri, null /* all columns, instead of projection */, null, null, null);
    }

    @Override
    public void onLoaderReset(final Loader<Cursor> loader) {
        fileNameView.setText(null);
        fileTypeView.setText(null);
        fileSizeView.setText(null);
        filePath = null;
        fileStreamUri = null;
        statusOk = false;
    }

    @Override
    public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) {
        if (data != null && data.moveToNext()) {
            /*
             * Here we have to check the column indexes by name as we have requested for all. The order may be different.
             */
            Log.d("CURSOR", JSON.toJSONString(data));
            final String fileName = data.getString(data.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)/* 0 DISPLAY_NAME */);
            final int fileSize = data.getInt(data.getColumnIndex(MediaStore.MediaColumns.SIZE) /* 1 SIZE */);
            String filePath = null;
            final int dataIndex = data.getColumnIndex(MediaStore.MediaColumns.DATA);
            if (dataIndex != -1) {
                filePath = data.getString(dataIndex /* 2 DATA */);
                Log.d("onLoadFinished", filePath);
            }
            if (!TextUtils.isEmpty(filePath)) {
                this.filePath = filePath;
                Log.d("onLoadFinished", "second filepath" + filePath);
            }
            Log.d("FILE UPDATE INFO", "524");
            updateFileInfo(fileName, fileSize, fileType);
        } else {
            fileNameView.setText(null);
            fileTypeView.setText(null);
            fileSizeView.setText(null);
            filePath = null;
            fileStreamUri = null;
            fileStatusView.setText(R.string.dfu_file_status_error);
            statusOk = false;
        }
    }

    /**
     * Updates the file information on UI
     *
     * @param fileName file name
     * @param fileSize file length
     */
    private void updateFileInfo(final String fileName, final long fileSize, final int fileType) {
        fileNameView.setText(fileName);
        switch (fileType) {
            case DfuService.TYPE_AUTO:
                fileTypeView.setText(getResources().getStringArray(R.array.dfu_file_type)[0]);
                break;
            case DfuService.TYPE_SOFT_DEVICE:
                fileTypeView.setText(getResources().getStringArray(R.array.dfu_file_type)[1]);
                break;
            case DfuService.TYPE_BOOTLOADER:
                fileTypeView.setText(getResources().getStringArray(R.array.dfu_file_type)[2]);
                break;
            case DfuService.TYPE_APPLICATION:
                fileTypeView.setText(getResources().getStringArray(R.array.dfu_file_type)[3]);
                break;
        }
        fileSizeView.setText(getString(R.string.dfu_file_size_text, fileSize));
        fileScopeView.setText(getString(R.string.not_available));
        final String extension = this.fileType == DfuService.TYPE_AUTO ? "(?i)ZIP" : "(?i)HEX|BIN"; // (?i) =  case insensitive
        final boolean statusOk = this.statusOk = MimeTypeMap.getFileExtensionFromUrl(fileName).matches(extension);
        fileStatusView.setText(statusOk ? R.string.dfu_file_status_ok : R.string.dfu_file_status_invalid);
//		uploadButton.setEnabled(selectedDevice != null && statusOk);
        startUpdateButton.setEnabled(statusOk);

        // Ask the user for the Init packet file if HEX or BIN files are selected. In case of a ZIP file the Init packets should be included in the ZIP.
        if (statusOk) {
            if (fileType != DfuService.TYPE_AUTO) {
                scope = null;
                fileScopeView.setText(getString(R.string.not_available));
                new AlertDialog.Builder(this)
                        .setTitle(R.string.dfu_file_init_title)
                        .setMessage(R.string.dfu_file_init_message)
                        .setNegativeButton(R.string.no, (dialog, which) -> {
                            initFilePath = null;
                            initFileStreamUri = null;
                        })
                        .setPositiveButton(R.string.yes, (dialog, which) -> {
                            final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                            intent.setType(DfuService.MIME_TYPE_OCTET_STREAM);
                            intent.addCategory(Intent.CATEGORY_OPENABLE);
                            startActivityForResult(intent, SELECT_INIT_FILE_REQ);
                        })
                        .show();
            } else {
                new AlertDialog.Builder(this).setTitle(R.string.dfu_file_scope_title).setCancelable(false)
                        .setSingleChoiceItems(R.array.dfu_file_scope, 0, (dialog, which) -> {
                            switch (which) {
                                case 0:
                                    scope = null;
                                    break;
                                case 1:
                                    scope = DfuServiceInitiator.SCOPE_SYSTEM_COMPONENTS;
                                    break;
                                case 2:
                                    scope = DfuServiceInitiator.SCOPE_APPLICATION;
                                    break;
                            }
                        }).setPositiveButton(R.string.ok, (dialogInterface, i) -> {
                    int index;
                    if (scope == null) {
                        index = 0;
                    } else if (scope == DfuServiceInitiator.SCOPE_SYSTEM_COMPONENTS) {
                        index = 1;
                    } else {
                        index = 2;
                    }
                    fileScopeView.setText(getResources().getStringArray(R.array.dfu_file_scope)[index]);
                }).show();
            }
        }
    }

    /**
     * Called when the question mark was pressed
     *
     * @param view a button that was pressed
     */
    public void onSelectFileHelpClicked(final View view) {
        new AlertDialog.Builder(this)
                .setTitle(R.string.dfu_help_title)
                .setMessage(R.string.dfu_help_message)
                .setPositiveButton(R.string.ok, null)
                .show();
    }

    /**
     * Called when Select File was pressed
     *
     * @param view a button that was pressed
     */
    public void onSelectFileClicked(final View view) {
        fileTypeTmp = fileType;
        int index = 0;
        switch (fileType) {
            case DfuService.TYPE_AUTO:
                index = 0;
                break;
            case DfuService.TYPE_SOFT_DEVICE:
                index = 1;
                break;
            case DfuService.TYPE_BOOTLOADER:
                index = 2;
                break;
            case DfuService.TYPE_APPLICATION:
                index = 3;
                break;
        }
        // Show a dialog with file types
        new AlertDialog.Builder(this)
                .setTitle(R.string.dfu_file_type_title)
                .setSingleChoiceItems(R.array.dfu_file_type, index, (dialog, which) -> {
                    switch (which) {
                        case 0:
                            fileTypeTmp = DfuService.TYPE_AUTO;
                            break;
                        case 1:
                            fileTypeTmp = DfuService.TYPE_SOFT_DEVICE;
                            break;
                        case 2:
                            fileTypeTmp = DfuService.TYPE_BOOTLOADER;
                            break;
                        case 3:
                            fileTypeTmp = DfuService.TYPE_APPLICATION;
                            break;
                    }
                })
                .setPositiveButton(R.string.ok, (dialog, which) -> openFileChooser())
                .setNeutralButton(R.string.dfu_file_info, (dialog, which) -> {
                    final ZipInfoFragment fragment = new ZipInfoFragment();
                    fragment.show(getSupportFragmentManager(), "help_fragment");
                })
                .setNegativeButton(R.string.cancel, null)
                .show();
    }

    private void openFileChooser() {
        final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType(fileTypeTmp == DfuService.TYPE_AUTO ? DfuService.MIME_TYPE_ZIP : DfuService.MIME_TYPE_OCTET_STREAM);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        if (intent.resolveActivity(getPackageManager()) != null) {
            // file browser has been found on the device
            startActivityForResult(intent, SELECT_FILE_REQ);
        } else {
            // there is no any file browser app, let's try to download one
            final View customView = getLayoutInflater().inflate(R.layout.app_file_browser, null);
            final ListView appsList = customView.findViewById(android.R.id.list);
            appsList.setAdapter(new FileBrowserAppsAdapter(this));
            appsList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
            appsList.setItemChecked(0, true);
            new AlertDialog.Builder(this)
                    .setTitle(R.string.dfu_alert_no_filebrowser_title)
                    .setView(customView)
                    .setNegativeButton(R.string.no, (dialog, which) -> dialog.dismiss())
                    .setPositiveButton(R.string.ok, (dialog, which) -> {
                        final int pos = appsList.getCheckedItemPosition();
                        if (pos >= 0) {
                            final String query = getResources().getStringArray(R.array.dfu_app_file_browser_action)[pos];
                            Log.d("Query",query);
                            final Intent storeIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(query));
                            startActivity(storeIntent);
                        }
                    })
                    .show();
        }
    }


    private void showUploadCancelDialog() {
        final LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
        final Intent pauseAction = new Intent(DfuService.BROADCAST_ACTION);
        pauseAction.putExtra(DfuService.EXTRA_ACTION, DfuService.ACTION_PAUSE);
        manager.sendBroadcast(pauseAction);

        final UploadCancelFragment fragment = UploadCancelFragment.getInstance();
        fragment.show(getSupportFragmentManager(), TAG);
    }

    /**
     * Callback of CONNECT/DISCONNECT button on DfuActivity
     */
    public void onConnectClicked(final View view) {
        if (isBLEEnabled()) {
            showDeviceScanningDialog();
        } else {
            showBLEDialog();
        }
    }

    private void onTransferCompleted() {
        clearUI(true);
        showToast(R.string.dfu_success);
    }

    public void onUploadCanceled() {
        clearUI(false);
        showToast(R.string.dfu_aborted);
    }

    @Override
    public void onCancelUpload() {

    }

    private void showErrorMessage(final String message) {
        clearUI(false);
        showToast("Upload failed: " + message);
    }

    private void clearUI(final boolean clearDevice) {

        selectFileButton.setEnabled(true);
        startUpdateButton.setEnabled(false);
        startUpdateButton.setText(R.string.dfu_action_upload);
        // Application may have lost the right to these files if Activity was closed during upload (grant uri permission). Clear file related values.
        fileNameView.setText(null);
        fileTypeView.setText(null);
        fileScopeView.setText(null);
        fileSizeView.setText(null);
        fileStatusView.setText(R.string.dfu_file_status_no_file);
        filePath = null;
        fileStreamUri = null;
        initFilePath = null;
        initFileStreamUri = null;
        statusOk = false;
    }

    private void showToast(final int messageResId) {
        Toast.makeText(this, messageResId, Toast.LENGTH_SHORT).show();
    }

    private void showToast(final String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    public void onUpdateStart() {
        if(fileStreamUri == null) {
            new AlertDialog.Builder(this)
                    .setTitle("未选中升级文件！")
                    .setPositiveButton("确认", (dialog, which) -> dialog.dismiss())
                    .show();
        } else {
            new AlertDialog.Builder(this)
                    .setTitle("是否确认执行批量升级任务？")
                    .setNegativeButton("取消", (dialog, which) -> dialog.dismiss())
                    .setPositiveButton("确认", (dialog, which) -> {
                        startBatchUpdate();
                    })
                    .show();
        }
    }
   //批量升级
    public void startBatchUpdate() {
        Intent newIntent = new Intent(SelectedFileActivity.this, BatchDfuActivity.class);
        Bundle newBundle = new Bundle();
        newBundle.putStringArrayList("SELECTED_DEVICES", selectedDevices);
        newBundle.putParcelable("FILEURI", fileStreamUri);
        newIntent.putExtras(newBundle);
        startActivity(newIntent);
    }
}
